import Game,{ config } from './game';
import { UBoolean, UFontSize, ULength, UString, UStringArray, UNumber, UAny } from './UVariable';
import { PropertyAnnotation, Constructor } from './annotation';


export class Alias {
    value: string[];
    constructor( value:string[]) {
        this.value = value;
    }
}

export function AliasProperty(...alias: string[]) {
    return PropertyAnnotation(new Alias(alias));
}

export function RegisterGameNode(...alias: string[]) {
    return function(target: any) {
        for (let tag of alias) {
            if (RegisterGameNode.nameMap[tag]) {
                throw new Error(`存在多个相同名字的节点别名 别名:${tag} 类:${target.name},${RegisterGameNode.nameMap[tag].name}`);
            }
            else {
                RegisterGameNode.nameMap[tag] = target;
            }
        }
        // RegisterGameNode.nameMap
    }
}
RegisterGameNode.nameMap = <{[x: string]: Constructor<GameNode>}>{}

export class GameNode {
    game!: Game;
    tag!: string;
    // @AliasProperty("i", "id")
    id!: string;
    children: GameNode[] = [];
    el?: HTMLElement;
    notDestroy = new UBoolean(false);
    parent!: GameNode;
    /**
     * 不深度寻找
     */
    findByTag<T>(tag: string): T[] {
        return <T[]>this.children.filter((o) => o.tag == tag);
    }

    create() {
        if (this.el! == null) {
            this.onCreateOrUpdate(true);
            for (let child of this.children) {
                child.create();
            }
        }
    }

    update() {
        if (this.el! != null) {
            this.onCreateOrUpdate(false);
            for (let child of this.children) {
                child.update();
            }
        }
    }
    protected onCreateOrUpdate(isCreate: boolean) {}

    destroy() {
        if (this.notDestroy.calcValue()) return;
        if (this.el) {
            this.el.remove();
            this.el = undefined;
        }
        for (let child of this.children) {
            child.destroy();
        }
    }

    forcedDestruction() {
        if (this.el) {
            this.el.remove();
            this.el = undefined;
        }
        for (let child of this.children) {
            child.forcedDestruction();
        }
    }

    /**不包含自身 */
    depthChildForEach(callback: (iap: GameNode) => void) {
        for (let child of this.children) {
            callback(child);
            child.depthChildForEach(callback);
        }
    }
    /**包含自身 */
    depthForEach(callback: (iap: GameNode) => void) {
        callback(this);
        this.depthChildForEach(callback);
    }
}

@RegisterGameNode(":IAP")
export class BodyNode extends GameNode {
    static tag = ':IAP';
    version!: string;
    @AliasProperty("n")
    nextStepID = new UString();
    /**封面 */
    @AliasProperty("bg", "cover")
    cover = new UString();
    // size!: Vector2;
    constructor() {
        super();
        this.tag = BodyNode.tag;
    }

    update(): void {
        // this.onCreateOrUpdate(false);
        for (let child of this.children) {
            child.update();
        }
    }
}

@RegisterGameNode("step")
export class StepNode extends GameNode {
    static tag = 'step';
    @AliasProperty("n")
    nextStepID = new UString();
    @AliasProperty("bg")
    bg = new UString();
    @AliasProperty("se")
    se = new UString();
    @AliasProperty("bgm")
    bgm = new UString();
    @AliasProperty("video")
    video = new UString();
    @AliasProperty("effect")
    effects = new UStringArray([]);
    @AliasProperty("loop")
    loop = new UBoolean(false);
    @AliasProperty("tx")
    text = new UString();
    //e-dy
    @AliasProperty("edy")
    enterDestroy = new UStringArray();
    //l-dy
    @AliasProperty("ldy")
    leaveDestroy = new UStringArray();
    @AliasProperty("dur", "duration")
    duration = new UNumber();

    constructor() {
        super();
        this.tag = StepNode.tag;
    }

    onCreateOrUpdate(isCreate: boolean): void {
        if(isCreate) {
            if (this.video.calcValue()) {
                this.game.playVideo(this.video.value);
                this.game.hideBG();
            } else if (this.bg.calcValue()) {
                this.game.stopAndHideVideo();
                this.game.showBG(this.bg.value);
            }
            if (this.se.calcValue()) {
                this.game.playSe(this.se.value, this.loop.calcValue());
            }
            if (this.text.calcValue()) {
                this.game.showGalText(this.text.value);
            } else {
                this.game.hideGalText();
            }
            if (this.bgm.calcValue()) {
                this.game.playBGM(this.bgm.value);
            }
            // if (this.fontSize) {
                // this.fontSize = this.fontSize;
                // this.playBGM(this.bgm);
            // }
            this.game.playEffects(this.effects.calcValue());
        }
    }

    update(): void {
        // this.onCreateOrUpdate(false);        
        for (let child of this.children) {
            child.update();
        }
    }
}
export abstract class VisualNode extends GameNode {
    
    @AliasProperty("l")
    left = new ULength();
    @AliasProperty("t")
    top = new ULength();
    @AliasProperty("r")
    right = new ULength();
    @AliasProperty("b")
    bottom = new ULength();
    @AliasProperty("w")
    width = new ULength();
    @AliasProperty("h")
    height = new ULength();
   
    @AliasProperty("hover")
    hoverText = new UString();
    @AliasProperty("se")
    se = new UString();
    // @AliasProperty("icon")
    // icon = new UString();
    @AliasProperty("tx", "text")
    text = new UString();
    @AliasProperty("n")
    nextStepID = new UString();
    @AliasProperty("loop")
    loop = new UBoolean();
    @AliasProperty("effect")
    effects = new UStringArray([]);
    @AliasProperty("bg", "background")
    bg = new UString();

    @AliasProperty("fs", "font-size")
    fontColor = new UString();

    //边框
    @AliasProperty("border")
    border = new UBoolean(false);
    //换行模式 vertical-rl 缩减 v-rl,vertical-lr 缩减 v-lr
    @AliasProperty("wm", "writing-mode")
    writingMode = new UString(); 
    /**文字对齐 */
    @AliasProperty("ta", "text-align")
    textAlign = new UString(Align.center);

    //cdy
    @AliasProperty("cdy")
    clickDestroy = new UStringArray();


    @AliasProperty("cursor")
    cursor = new UString();
    @AliasProperty("fs", "font-size")
    fontSize = new UFontSize();

    @AliasProperty("z-i","z-index")
    zIndex = new UNumber(config.zIndex.btn);

    @AliasProperty("vis", "visible")
    visible = new UBoolean(true);

    @AliasProperty("c-exec", "click-exec")
    /** 点击执行 */
    clickExec = new UAny();

    textEl!: HTMLElement;

    constructor() {
        super();
    }

    onCreateOrUpdate(isCreate: boolean): void {
        var game = this.game;
        if(isCreate) {
            this.el! = document.createElement('div');
            this.el.style.position = "absolute";
        }
       

        if(this.visible.needChange(isCreate)) {
            this.el!.style.display = this.visible.value ? "" : "none"
        }

        if (this.text.srcValueIsValid()) {
            if(isCreate) {
                this.el!!.classList.add('iap-text-btn');
                this.textEl = document.createElement("div");
                this.textEl.style.position = "absolute"
                this.textEl.style.textAlign = "left"
                this.el!.appendChild(this.textEl);
            }
            if(this.text.needChange(isCreate)) {
                this.textEl.innerHTML = this.text.value;
            }
           
            if(this.writingMode.needChange(isCreate)) {
                if(this.writingMode.value == "v-rl" || this.writingMode.value == "vertical-rl") {
                    this.textEl.style.writingMode = "vertical-rl"
                }
                else if(this.writingMode.value == "v-lr" || this.writingMode.value == "vertical-lr") {
                    this.textEl.style.writingMode = "vertical-lr";
                }
                else {
                    this.textEl.style.writingMode = "";
                }
            }
            if(this.textAlign.needChange(isCreate)) {
                switch(this.textAlign.value) {
                    case Align.Left:
                        this.textEl.style.left = "0";
                        this.textEl.style.top = "50%";
                        this.textEl.style.transform = "translate(0, -50%)";
                        break;
                    case Align.LeftTop:
                        this.textEl.style.left = "0";
                        this.textEl.style.top = "0";
                        this.textEl.style.transform = "translate(0,0)";
                        break;
                    case Align.LeftBottom:
                        this.textEl.style.left = "0";
                        this.textEl.style.bottom = "0";
                        this.textEl.style.transform = "translate(0,0)";
                        break;
                    case Align.Right:
                        this.textEl.style.right = "0";
                        this.textEl.style.top = "50%";
                        this.textEl.style.transform = "translate(0,-50)";
                        break;
                    case Align.RightTop:
                        this.textEl.style.right = "0";
                        this.textEl.style.top = "0";
                        this.textEl.style.transform = "translate(0,0)";
                        break;
                    case Align.RightBottom:
                        this.textEl.style.right = "0";
                        this.textEl.style.bottom = "0";
                        this.textEl.style.transform = "translate(0,0)";
                        break;
                    case Align.Top:
                        this.textEl.style.left = "50%";
                        this.textEl.style.top = "0";
                        this.textEl.style.transform = "translate(-50%,0)";
                        break;
                    case Align.bottom:
                        this.textEl.style.left = "0";
                        this.textEl.style.bottom = "0";
                        this.textEl.style.transform = "translate(-50%,0)";
                        break;
                    default:
                        this.textEl.style.left = "50%";
                        this.textEl.style.top = "50%";
                        this.textEl.style.transform = "translate(-50%,-50%)";
                        break;
                }
            }
           
            if(this.fontColor.needChange(isCreate)) {
                this.textEl.style.color = this.fontColor.value;
            }
            if(this.fontSize.needChange(true)) {
                this.textEl.style.fontSize = this.fontSize.value;
            }
        }
        
        if(this.bg.srcValueIsValid() && this.bg.needChange(isCreate)) {
            this.el!.style.backgroundImage = `url("${this.bg.value}")`;
            this.el!.style.backgroundSize = "100% 100%";
        }
        else if(isCreate) {
            //透明的
            this.el!.style.background = 'transparent';
        }

        if(this.cursor.srcValueIsValid() && this.cursor.needChange(isCreate)) {
            this.el!.style.cursor = `url(${this.cursor}),auto`;
        }
        if (this.left.srcValueIsValid() && this.left.needChange(isCreate)) {
            this.el!.style.left = this.left.value;
        }
        if (this.top.srcValueIsValid() && this.top.needChange(isCreate)) {
            this.el!.style.top = this.top.value;
        }
        if (this.right.srcValueIsValid() && this.right.needChange(isCreate)) {
            this.el!.style.right = this.right.value;
        }
        if (this.bottom.srcValueIsValid() && this.bottom.needChange(isCreate)) {
            this.el!.style.bottom = this.bottom.value;
        }
        if (this.width.srcValueIsValid() && this.width.needChange(isCreate)) {
            this.el!.style.width = this.width.value;
        }
        if (this.height.srcValueIsValid() && this.height.needChange(isCreate)) {
            this.el!.style.height = this.height.value;
        }
        if(isCreate) {
            game.body.appendChild(this.el!);
        }


        if(this.zIndex.needChange(isCreate)) {
            this.el!.style.zIndex = this.zIndex.value + "";
        }
        

        if(isCreate) {
            this.el!.addEventListener('click', (e) => {
                if(this.nextStepID.calcValue()) {
                    game.nextSceneById(this.nextStepID.value);
                }
                if (this.se.value) {
                    game.playSe(this.se.calcValue(), this.loop.calcValue());
                }
                game.forcedDestroy(this.clickDestroy.calcValue());
                game.playEffects(this.effects.calcValue());
                this.clickExec.calcValue();
                e.stopPropagation();
                return true;
            });
        }

        if (isCreate && this.hoverText.srcValueIsValid()) {
            this.el!.addEventListener('mousemove', (e) =>  {
                game.hoverTextEl.innerHTML = this.hoverText.value;

                var bound = game.body.getBoundingClientRect();
                game.hoverTextEl.style.left = e.clientX - bound.left + 15 + 'px';
                game.hoverTextEl.style.top = e.clientY - bound.top + 15 + 'px';
                game.hoverTextEl.innerHTML = this.hoverText.calcValue();
                game.hoverTextEl.style.display = '';
                e.stopPropagation();
                return true;
            });
            this.el!.addEventListener('mouseleave', function (e) {
                game.hoverTextEl.style.display = 'none';
                e.stopPropagation();
                return true;
            });
        }
        
    }
}

@RegisterGameNode("btn", "button")
export class ButtonNode extends VisualNode {
    static tag = "Button"
    constructor() {
        super();
        this.tag = ButtonNode.tag;
    }
}

// @RegisterGameNode("tou", "touch")
// export class TouchNode extends VisualNode {
//     static tag = "Button"
//     constructor() {
//         super();
//         this.tag = ButtonNode.tag;
//     }
// }

// @RegisterGameNode("tou", "touch")
// export class TouchNode extends VisualNode {
//     static tag = "Button"
//     constructor() {
//         super();
//         this.tag = ButtonNode.tag;
//     }
// }


/** opt选项 */
@RegisterGameNode("opt", "option")
export class OptionNode extends GameNode {
    static tag : string = "option";
    
    @AliasProperty("tx", "text")
    text = new UString("");
    @AliasProperty("n")
    nextStepID = new UString();
    constructor() {
        super();
        this.tag = OptionNode.tag;
    }
    onCreateOrUpdate(isCreate: boolean): void {
        if(isCreate) {
            this.el! = document.createElement('div');
            this.el!.style.zIndex = '' + config.zIndex.option;
            this.el!.classList.add('iap-text-menuitem');
            this.el!.textContent = this.text.calcValue();
            this.game.optionGroup.appendChild(this.el!);
            this.el!.addEventListener("click", (e) => {
                if(this.nextStepID.srcValueIsValid()) {
                    this.game.nextSceneById(this.nextStepID.calcValue());
                }
                e.stopPropagation();
                return true;
            })
        }
        else {

        }
       
    }
}

// export class Bound {
//     left!: number;
//     top!: number;
//     right!: number;
//     bottom!: number;
//     width!: number;
//     height!: number;
// }

export class Vector2 {
    x: number;
    y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    static parse(str: string) {
        var split = str.split(",")
        return new Vector2(parseFloat(split[0]), parseFloat(split[1]));
    }
}

export enum Align{
    LeftTop = "lt",
    RightTop = "rt",
    Top = "t",
    Left = "l",
    Right = "r",
    center = "c",
    bottom = "b",
    LeftBottom = "lb",
    RightBottom = "rb",
}